import axios from "axios";
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
interface IAxiosOres<T> {
  data: T;
  status: boolean | number;
}
type IAxiosOProjectConfig = {
  noToken: boolean;
};
interface HFInstanceInterceptors<T, U> {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig;
  requestInterceptorCatch?: (err: any) => any;
  responseInterceptor?: (res: T) => U;
  responseInterceptorCatch?: (error: any) => U | Error | undefined;
}

interface HFRequestConfig<T, U> extends AxiosRequestConfig {
  interceptors?: HFInstanceInterceptors<T, U>;
}
// 调用顺序 接口请求-实例请求-全局请求-全局响应-实例响应-接口响应
type IAxiosOHandleConfigFn = (<T>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>) => void) | null;
class HFRequest {
  handleConfigFn: IAxiosOHandleConfigFn;
  instance: AxiosInstance;
  constructor(config: HFRequestConfig<AxiosResponse, IAxiosOres<any>>, handleConfigFn: IAxiosOHandleConfigFn) {
    this.handleConfigFn = handleConfigFn;
    this.instance = axios.create(config);
    // 全局的拦截器
    this.instance.interceptors.request.use(
      (config) => {
        return config;
      },
      (err) => {
        const res = err;
        if (res instanceof Error) throw res;
        return res;
      }
    );
    this.instance.interceptors.response.use(
      (res) => {
        return res;
      },
      (err) => {
        const res = err;
        if (res instanceof Error) throw res;
        return res;
      }
    );
    // 实例的拦截器
    this.instance.interceptors.request.use(config.interceptors?.requestInterceptor, config.interceptors?.requestInterceptorCatch);
    this.instance.interceptors.response.use(config.interceptors?.responseInterceptor, config.interceptors?.responseInterceptorCatch);
  }

  request<T>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>, projectConfig?: IAxiosOProjectConfig) {
    if (config.interceptors?.requestInterceptor) {
      config = config.interceptors.requestInterceptor(config);
    }
    if (!projectConfig?.noToken && this.handleConfigFn) {
      this.handleConfigFn(config);
    }
    return new Promise<IAxiosOres<T>>((resolve, reject) => {
      this.instance
        .request<any, IAxiosOres<T>>(config)
        .then((res) => {
          if (config.interceptors?.responseInterceptor) {
            res = config.interceptors.responseInterceptor(res) as IAxiosOres<T>;
          }
          resolve(res);
        })
        .catch((err) => {
          if (config.interceptors?.responseInterceptorCatch) {
            err = config.interceptors.responseInterceptorCatch(err);
          }
          reject(err);
        });
    });
  }

  get<T = any>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>, projectConfig?: IAxiosOProjectConfig) {
    return this.request<T>({ ...config, method: "GET" }, projectConfig);
  }

  post<T = any>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>, projectConfig?: IAxiosOProjectConfig) {
    const obj: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>> = {
      ...config,
      method: "POST"
    };
    return this.request<T>(obj, projectConfig);
  }

  delete<T = any>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>, projectConfig?: IAxiosOProjectConfig) {
    return this.request<T>({ ...config, method: "DELETE" }, projectConfig);
  }

  put<T = any>(config: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>>, projectConfig?: IAxiosOProjectConfig) {
    const obj: HFRequestConfig<IAxiosOres<T>, IAxiosOres<T>> = {
      ...config,
      method: "PUT"
    };
    return this.request<T>(obj, projectConfig);
  }
}

export default HFRequest;
